#include <stdio.h>
#include <stdlib.h>
#include <glib-2.0/glib.h>
#include <pthread.h>
#include "superpeer.h"
#include "structures.h"

extern GList * listaPeer;
extern GList * listaChunk;

int compareID(gconstpointer a, gconstpointer b){
    //ritorna negativo se a è minore di b
    //ritorna zero se sono uguali
    //ritorna maggiore di zero se b è maggiore di a
    struct chunkPoss *aa = (struct chunkPoss *)a;
    struct chunkPoss *bb = (struct chunkPoss *)b;
    return strcmp(aa->ID,bb->ID);
    //for(i=0;i<16;i++){
     //   countA += countA+(aa->ID[i]^aa->ID[i+1]);
      //  countB += countB+(bb->ID[i]^bb->ID[i+1]);
    //}
    //if(countA<countB) return -1;
    //if(countA==countB) return 0;
    //if(countA>countB) return 1;
}
int compareAddrAndPort(gconstpointer a, gconstpointer b){
    //ritorna negativo se a è minore di b
    //ritorna zero se sono uguali
    //ritorna maggiore di zero se b è maggiore di a
    struct peer *pa = (struct peer *)a;
    struct peer *pb = (struct peer *)b;
    if(strcmp(pa->addr,pb->addr)==0){
        if(pa->port==pb->port){
            return 0;
        }
        else
            return -1;
    }
    else return 1;
}
struct elemPoss * insElemPoss(struct chunkPoss * chk, struct peer * p){

    //inserisci in testa alla lista possessori del chunkPoss chk un campo
    //elemPoss con all'interno il campo peer * p

    struct elemPoss * el = (struct elemPoss *)malloc(sizeof(struct elemPoss));

    printf("Sono prima del primo lock si insElemPoss\n");

    pthread_mutex_init(&el->mutexPoss1, NULL);

    pthread_mutex_lock(&el->mutexPoss1);

    printf("Sono subito dopo il primo lock di insElemPoss\n");

    el->p=p;

    //inserisco in testa

    printf("Sono prima del lock della sentinella dentro insElemPoss\n");

    pthread_mutex_lock(&chk->firstPoss->mutexPoss1); //blocco sentinella

    printf("Sono subito dopo il lock della sentinella dentro insElemPoss\n");

    struct elemPoss *tmp = chk->firstPoss->next;

    printf("Sono prima del blocco di tmp dentro insElemPoss\n");

    pthread_mutex_lock(&tmp->mutexPoss1); //blocco elemento in testa precedentemente

    printf("Sono dopo il blocco di tmp dentro insElemPoss\n");

    chk->firstPoss->next=el;

    tmp->prev=el;

    el->next=tmp;

    el->prev=chk->firstPoss;

    //sblocco tutto

    pthread_mutex_unlock(&tmp->mutexPoss1);

    pthread_mutex_unlock(&chk->firstPoss->mutexPoss1);

    pthread_mutex_unlock(&el->mutexPoss1);

    return el;

}
int removeElemPoss(struct elemPoss * daRimuovere){
    struct elemPoss * prev = daRimuovere->prev;
    struct elemPoss * next = daRimuovere->next;
    pthread_mutex_lock(&prev->mutexPoss1);
    prev->next=next;
    pthread_mutex_unlock(&prev->mutexPoss1);
    if(next!=NULL){
        pthread_mutex_lock(&next->mutexPoss1);
        next->prev=prev;
        pthread_mutex_unlock(&next->mutexPoss1);

    }
    pthread_mutex_unlock(&daRimuovere->mutexPoss1);
    free(daRimuovere);//giusto farla qui?
}
void rimuoviChunkSenzaPossessori(){
    int i=0;
    GList * temp=NULL;
    for(i=0;i<g_list_length(listaChunk);i++){
        struct chunkPoss * chkP = (struct chunkPoss *)g_list_nth_data(listaChunk,i);
        pthread_mutex_lock(&chkP->mutexChunkPoss);
        if(chkP->firstPoss->next==NULL){
            //procedi alla cancellazione del chunk dalla lista
            temp=g_list_nth(listaChunk,i);
            listaChunk=g_list_delete_link(listaChunk,temp);
            pthread_mutex_unlock(&chkP->mutexChunkPoss);
            temp=NULL;
            free(chkP);
            i--;
        }
        else{
            pthread_mutex_unlock(&chkP->mutexChunkPoss);
        }
       
    }
}
int addPeerWithChunks(struct peer * p, int nChunk, struct chunkLight * cl){

    //nella struttura peer manca il contenuto della GList *poss
    //ho bisogno di un ciclo in cui si verifica se il chunk posseduto già esiste
    //e aggiunge il peer nella lista elemPoss tramite un opportuna struttura dati

    struct chunkLight *tmpChunk=cl;

    GList *temp=NULL;

    int i=0;

    gint (*compareFunction) (gconstpointer a, gconstpointer b);

    compareFunction=compareID;

    //creo un puntatore gpointer per il peer passato come parametro
    gpointer *pg=(gpointer)p;

    //aggiungo il puntatore al peer creato nella lista dei peer

    //devo bloccare l'ultimo elemento della lista

    GList * tempLock = NULL;
    tempLock = g_list_last(listaPeer);
    struct peer * pT=NULL;
    if(tempLock!=NULL){
        printf("Nella lista peer c'e' qualcosa\n");
        pT = (struct peer *)tempLock->data;
        pthread_mutex_lock(&pT->mutexPeer);//blocco l'ultimo campo
    }

    listaPeer = g_list_insert(listaPeer,pg,-1);

    if(tempLock!=NULL){
        pthread_mutex_unlock(&pT->mutexPeer); //sblocco l'attuale penultimo campo
    }
    for(i=0;i<nChunk;i++){

        //controllo se il chunk esiste

        struct chunkPoss * chkTmp = malloc(sizeof(struct chunkPoss));

        strcpy(chkTmp->ID,tmpChunk->ID);

        strcpy(chkTmp->IDF,tmpChunk->IDF);

        pthread_mutex_init(&chkTmp->mutexChunkPoss, NULL);

        gpointer *tmpP=(gpointer)chkTmp;

        temp=g_list_find_custom(listaChunk,tmpP,compareFunction);

        if(temp!=NULL){
            printf("Sto inserendo un possessore nel chunk esistente\n");
            //il chunk esiste
            //inserisco un nuovo elemPoss in testa alla lista
            struct elemPoss * el = insElemPoss((struct chunkPoss*)temp->data, p);
            free(chkTmp);
            gpointer nuovoElemento = (gpointer)el;
            p->poss=g_list_insert(p->poss,nuovoElemento,-1);

        }
        else{
            printf("Il chunk %s non esiste e lo aggiungo\n",chkTmp->ID);
            //il chunk non esiste
            //utilizzo chkTmp per inserire il nuovo chunk nella lista
            //come prima cosa devo preparare la struttura sentinella per la
            //lista dei possessori

            struct elemPoss * sentinel = (struct elemPoss *)malloc(sizeof(struct elemPoss));

            sentinel->next=NULL;

            sentinel->prev=NULL;

            sentinel->p=NULL;

            pthread_mutex_init(&sentinel->mutexPoss1, NULL);

            chkTmp->firstPoss = sentinel;

            //una volta creato il chunk posso aggiungere il campo relativo al
            //possessore. Tale inserimento viene fatto con l'opportuna funzione
            //che opera su liste a doppio puntatore di elemPoss
            sentinel->next=(struct elemPoss *)malloc(sizeof(struct elemPoss));
            struct elemPoss * tmpNewChunk = sentinel->next;
            tmpNewChunk->next=NULL;
            tmpNewChunk->prev=sentinel;
            tmpNewChunk->p=p;
            pthread_mutex_init(&tmpNewChunk->mutexPoss1, NULL);
            gpointer *tmpNewChunkG = (gpointer)tmpNewChunk;
            p->poss=g_list_insert(p->poss,tmpNewChunkG,-1);
            gpointer * nuovoChunk = (gpointer)chkTmp;
            listaChunk = g_list_insert_sorted(listaChunk,nuovoChunk,compareFunction);
        }
        tmpChunk++;
    }

    return 1;

};
int removePeer(struct peerLight *pl){
    //come primo passo si cerca il peer nella lista
    //si provvede quindi a tradurre il peerLight come peer
    GList *temp = NULL;
    struct peer *p = (struct peer *)calloc(1,sizeof(struct peer));
    strcpy(p->addr,pl->addr);
    p->port=pl->port;
    //si forma una nuova funzione per il confronto
    gint (*compareFunctionPeer) (gconstpointer a, gconstpointer b);
    compareFunctionPeer=compareAddrAndPort;
    gpointer pG = (gpointer)p;
    temp=g_list_find_custom(listaPeer,pG,compareFunctionPeer);
    //a questo punto controlliamo se esiste il peer da scollegare
    free(p);
    p=NULL;
    if(temp!=NULL){
        //sappiamo che esiste, si provvede a rimuovere ogni occorrenza di tale
        //peer nella lista dei chunkPosseduti; si accede direttamente dalla GList
        p=temp->data;
        pthread_mutex_lock(&p->mutexPeer);
        printf("Ho bloccato il peer: %s\n",p->addr);
        //per ogni campo devo accedere all'elemPoss e rimuoverlo sempre tenendo
        //presente che il campo è una lista a doppio puntatore
        int i=0;
        for(i=g_list_length(p->poss)-1;i>=0;i--){
            //scorro la lista dalla fine verso l'inizio; per prima cosa rimuovo
            //fisicamente il campo elemPoss dalla lista dei possessori ed in
            //seguito lo rimuovo dalla lista p->poss
            struct elemPoss * tmp = (struct elemPoss *)g_list_nth_data(p->poss,i);
            if(tmp!=NULL){
                pthread_mutex_lock(&tmp->mutexPoss1);
            //effettuo la rimozione fisica (struct chunkPoss*)temp->datatramite l'opportuna funzione
            removeElemPoss(tmp);
            }
            //viene fatto l'unlock di tmp dentro tale funzione

            //rimuovo tutto alla fine
        }
        //ho tolto tutti i campi della lista
        g_list_free(p->poss);
        //a questo punto posso rimuovere il peer
        pG = (gpointer)p;
        pthread_mutex_unlock(&p->mutexPeer);
        printf("Ho sbloccato il peer: %s\n",p->addr);
        listaPeer=g_list_delete_link(listaPeer,temp);
        //faccio un controllo se ci sono chunk senza possessori, in questo caso
        //li cancello
        rimuoviChunkSenzaPossessori();
        //g_list_remove(listaPeer,pG);
    }
    else printf("Peer %s, %d non presente\n",p->addr,p->port);
};
void printPeerList(){

    int i=0;
    int lun = g_list_length(listaPeer);
    for(i=0;i<lun;i++){

        struct peer *tmp = (struct peer *)g_list_nth_data(listaPeer,i);

        printf("Indirizzo peer: %s, porta peer: %d\n",tmp->addr,tmp->port);

    }
}
void printChunkPossList(){
    int i=0;

    for(i=0;i<g_list_length(listaChunk);i++){

        struct chunkPoss * chkTmp = (struct chunkPoss *)g_list_nth_data(listaChunk,i);

        printf("Il chunk presente e' : %s\n", chkTmp->ID);

        printPossList(chkTmp);

        printf("\n");

    }
}
void printPossList(struct chunkPoss * chk){

    struct elemPoss * tmp = chk->firstPoss;

    while(tmp->next!=NULL){

        tmp=tmp->next;

        printf("Possessore del chunk %s, %s\n",chk->ID,tmp->p->addr);

    }
}
